package com.middlegroup.subjectpay.servlet;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.middlegroup.common.exception.BusinessException;
import com.middlegroup.common.model.ResponseEntity;
import com.middlegroup.common.model.TransResult;
import com.middlegroup.common.utils.AesUtils;
import com.middlegroup.common.utils.RSAUtils;
import com.middlegroup.common.utils.SpringContextUtil;
import com.middlegroup.common.wrapper.EncryptedResponseWrapper;
import com.middlegroup.common.wrapper.ParameterRequestWrapper;
import com.middlegroup.common.enums.ConfigKeyEnums;
import com.middlegroup.subjectpay.entity.SubjectLog;
import com.middlegroup.subjectpay.service.SubjectChannelConfigService;
import com.middlegroup.subjectpay.service.SubjectLogService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author pc
 */
public class ApiGatewayServlet extends HttpServlet {
	private static final long serialVersionUID = -909785589890909548L;
	private static final String SIGN_TYPE = "SHA1WithRSA";

	private final Logger logger = LoggerFactory.getLogger(getClass());

	// 平台密钥
	private PrivateKey privateKey;
	// 主体端公钥
	private PublicKey publicKey;
	//支付中台公钥
	private PublicKey platformPublicKey;


	private SubjectLogService subjectLogService;

	private SubjectChannelConfigService subjectChannelConfigService;

	@Override
	public void init(ServletConfig config) throws ServletException {
		logger.debug("enter method");
		this.subjectChannelConfigService = SpringContextUtil.getBean(SubjectChannelConfigService.class);
		this.subjectLogService = SpringContextUtil.getBean(SubjectLogService.class);
		//加载支付中台的公钥
		ConfigKeyEnums configKeyEnums = ConfigKeyEnums.valueOf(ConfigKeyEnums.SUBJECT_PAYPLATFORM_RSA_PUB_KEY.name());
		String platformPubKeyStr = subjectChannelConfigService.getConfigString(configKeyEnums.getCategory(),configKeyEnums.getName());
		if (StringUtils.isBlank(platformPubKeyStr)){
			throw new BusinessException("中台公钥未配置!");
		}
		this.platformPublicKey = RSAUtils.readPublicKey(platformPubKeyStr);
		configKeyEnums = ConfigKeyEnums.valueOf(ConfigKeyEnums.SUBJECT_RSA_PRI_KEY.name());
		String privateKeyStr = subjectChannelConfigService.getConfigString(configKeyEnums.getCategory(),configKeyEnums.getName());
		if (StringUtils.isBlank(privateKeyStr)){
			throw new BusinessException("主体服务端私钥未配置!");
		}
		configKeyEnums = ConfigKeyEnums.valueOf(ConfigKeyEnums.SUBJECT_RSA_PUB_KEY.name());
		String publikKeyStr = subjectChannelConfigService.getConfigString(configKeyEnums.getCategory(),configKeyEnums.getName());
		if (StringUtils.isBlank(publikKeyStr)){
			throw new BusinessException("主体服务端公钥未配置!");
		}
		//加载相应主体的私钥
		this.privateKey = RSAUtils.readPrivateKey(privateKeyStr);
		//加载相应主体的公钥
		this.publicKey = RSAUtils.readPublicKey(publikKeyStr);

		logger.debug("exit method");
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		Long beginTime = System.currentTimeMillis();

		resp.setCharacterEncoding("UTF-8");
		resp.setContentType("application/json");
		//业务参数解密密钥，用我方私钥解密
		String aesKey = req.getParameter("aesKey");
		String method = req.getParameter("method");
		String subjectCode = req.getParameter("subjectCode");
		String sign = req.getParameter("sign");
		String reqData = req.getParameter("reqData");
		SubjectLog subjectLog = new SubjectLog("支付中台调用主体服务端",method,"");
		subjectLog.setAccount(subjectCode);
		subjectLog.setIp(req.getRemoteHost());
		boolean paramOk = true;
		String msg = null;
		if (StringUtils.isBlank(aesKey)){
			paramOk = false;
			msg = "缺少aesKey字段!";
		}
		if (StringUtils.isBlank(method)){
			paramOk = false;
			msg = "缺少method字段!";
		}
		if (StringUtils.isBlank(subjectCode)){
			paramOk = false;
			msg = "缺少subjectCode字段!";
		}
		if (StringUtils.isBlank(sign)){
			paramOk = false;
			msg = "缺少sign字段!";
		}
		if (!paramOk){
			ResponseEntity ret = new ResponseEntity(ResponseEntity.STATUS_FAIL,null,msg,null);
			String content = JSONObject.toJSONString(ret);
			resp.getWriter().write(content);
			//由于是在servlet中，故可以不手动关闭，防止导致其他filter异常
			Long endTime = System.currentTimeMillis();
			subjectLog.setDuration(endTime-beginTime);
			subjectLog.setMessage(msg);
			subjectLogService.insert(subjectLog);
			return;
		}

		//验证主体是否存在
		ConfigKeyEnums configKeyEnums = ConfigKeyEnums.valueOf(ConfigKeyEnums.SUBJECT_CODE.name());
		String dataBaseSubjectCode=subjectChannelConfigService.getConfigString(configKeyEnums.getCategory(),configKeyEnums.getName());
		if (StringUtils.isBlank(dataBaseSubjectCode)){
			//主体不存在
			ResponseEntity ret = new ResponseEntity(ResponseEntity.STATUS_FAIL,null,"主体不存在,请联系管理员配置!",null);
			String content = JSON.toJSONString(ret);
			logger.error("+++++ response plain:{}", content);
			// content is UTF-8 string
			resp.getWriter().write(content);
			Long endTime = System.currentTimeMillis();
			subjectLog.setDuration(endTime-beginTime);
			subjectLog.setMessage("主体不存在,请联系管理员配置!");
			subjectLogService.insert(subjectLog);
			return;
		}

		//验证主体是否正确
		if (!StringUtils.equals(dataBaseSubjectCode,subjectCode)){
			//主体校验失败
			ResponseEntity ret = new ResponseEntity(ResponseEntity.STATUS_FAIL,null,"主体校验失败,请联系管理员确认!",null);
			String content = JSON.toJSONString(ret);
			logger.error("+++++ response plain:{}", content);
			// content is UTF-8 string
			resp.getWriter().write(content);
			Long endTime = System.currentTimeMillis();
			subjectLog.setDuration(endTime-beginTime);
			subjectLog.setMessage("主体校验失败,请联系管理员确认!");
			subjectLogService.insert(subjectLog);
			return;
		}

		//验证签名
		Map<String,String> treeMap = new TreeMap<String,String>();
		treeMap.put("method",method);
		treeMap.put("subjectCode",subjectCode);
		treeMap.put("aesKey",aesKey);
		treeMap.put("reqData",reqData);
		String waitSignStr = JSONObject.toJSONString(treeMap);

		try {


			//使用支付中台的公钥签名
			Boolean signOK = RSAUtils.verifySign(SIGN_TYPE, waitSignStr.getBytes("UTF-8"), this.platformPublicKey, sign);
			logger.debug("+++++++ signOK:{}", signOK);
			if(! signOK){
				logger.error("+++++++ sign NOT OK, 缺少subjectCode字段:{}, data:{}, sign:{}", new Object[]{subjectCode, waitSignStr, sign});
				ResponseEntity ret = new ResponseEntity(ResponseEntity.STATUS_FAIL,null,"签名验证失败!",null);
				String respPlain = JSON.toJSONString(ret);
				logger.error("+++++ response plain:{}", respPlain);

				String content = respPlain;
				if(logger.isDebugEnabled()){
					logger.debug("+++++ reponse encrypted:{}", content);
				}
				// content is UTF-8 string
				resp.getWriter().write(content);
				//由于是在servlet中，故可以不手动关闭，防止导致其他filter异常
				//resp.getWriter().close();
				Long endTime = System.currentTimeMillis();
				subjectLog.setDuration(endTime-beginTime);
				subjectLog.setMessage("签名验证失败!");
				subjectLogService.insert(subjectLog);
				return;
			}

			Map<String, Object> fwParams = new HashMap<String, Object>();
			if(null != reqData){

				// 用主体私钥解密aesKey
				aesKey = RSAUtils.decryptByPrivateKey(aesKey,privateKey,"UTF-8");

				//用AES解密reqData
				reqData = AesUtils.decrypt(reqData,aesKey);

				JSONObject m = JSONObject.parseObject(reqData);

				for(String key : m.keySet()){
					fwParams.put(key, m.get(key));
				}
			}

			ParameterRequestWrapper reqWrapper = new ParameterRequestWrapper(req, fwParams);
			EncryptedResponseWrapper respWrapper = new EncryptedResponseWrapper(resp);

			RequestDispatcher dispatcher = req.getRequestDispatcher(method);
			req.setAttribute("subjectCode",subjectCode);
			dispatcher.forward(reqWrapper, respWrapper);
			// plain text
			String respPlain = respWrapper.getResult();
			String content = respPlain;
			String encrtptData = null;
			if (StringUtils.isBlank(respPlain)){
				TransResult transResult = new TransResult();
				//交易状态未知处理中,此处必须设置为交易状态处理中，防止重复交易
				transResult.setTransStatus(0);
				String waitEncrtpyData = JSONObject.toJSONString(transResult);
				encrtptData = AesUtils.encrypt(waitEncrtpyData,aesKey);
				ResponseEntity responseEntity = new ResponseEntity(ResponseEntity.STATUS_OK,null,"交易处理异常，请联系管理员!",encrtptData);
				content = JSONObject.toJSONString(responseEntity);
			}else {
				JSONObject ret = JSONObject.parseObject(respPlain);
				//加密data
				JSONObject data = ret.getJSONObject("data");
				if (null != data){
					String waitEncryptData = JSONObject.toJSONString(data);
					encrtptData = AesUtils.encrypt(waitEncryptData,aesKey);
					ret.put("data",encrtptData);

				}else {
					TransResult transResult = new TransResult();
					//交易状态未知处理中,此处必须设置为交易状态处理中，防止重复交易
					transResult.setTransStatus(0);
					String waitEncrtpyData = JSONObject.toJSONString(transResult);
					encrtptData = AesUtils.encrypt(waitEncrtpyData,aesKey);
					ret.put("data",encrtptData);
				}
				content = JSONObject.toJSONString(ret);
			}

			if(null != respPlain) {
				// content is UTF-8 string
				OutputStream out = resp.getOutputStream();
				out.write(content.getBytes("UTF-8"));
				out.close();
				Long endTime = System.currentTimeMillis();
				subjectLog.setDuration(endTime-beginTime);
				subjectLog.setMessage("成功");
				subjectLogService.insert(subjectLog);
			}
		} catch (Exception e) {
			logger.error("doPost error, parameter:", req.getParameterMap());
			logger.error("doPost error, stack:", e);
			Long endTime = System.currentTimeMillis();
			subjectLog.setDuration(endTime-beginTime);
			subjectLog.setMessage("异常"+e.getMessage());
			subjectLogService.insert(subjectLog);
		}
	}
}
